//Copyright (c) 2006-2007 Microsoft Corporation.  All rights reserved
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Reflection;

namespace InvocationFramework.Common.Library
{
    /// <summary>
    /// Helper para trabajar con Reflection
    /// </summary>
    internal static class ReflectionHelper
    {
        #region Public Methods

        /// <summary>
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static Type GetType( string type )
        {
            return Type.GetType( type, true );
        }
        
        /// <summary>
        /// Crea un objeto
        /// </summary>
        /// <param name="type">Tipo del objeto a crear</param>
        /// <returns>Una instancia del objeto si este se pudo crear, null de lo contrario</returns>
        public static object CreateObject( string type )
        {
            return CreateObject( GetType( type ) );
        }

        /// <summary>
        /// Crea un objeto
        /// </summary>
        /// <param name="type">Tipo del objeto a crear</param>
        /// <returns>Una instancia del objeto si este se pudo crear, null de lo contrario</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes" )]
        public static object CreateObject( Type type )
        {
            object result;

            try
            {
                result = Activator.CreateInstance( type );
            }
            catch
            {
                result = null;
            }

            return result;
        }

        /// <summary>
        /// Permite invocar un metodo
        /// </summary>
        /// <param name="methodName">Nombre del metodo a invocar</param>
        /// <param name="methodType">Tipo donde se encuentra el metodo</param>
        /// <param name="methodParameters">Parametros del metodo</param>
        /// <returns>Resultado de la ejecucion del metodo</returns>
        public static object InvokeMember( string methodName, Type methodType, params object[] methodParameters )
        {
            object instance = ReflectionHelper.CreateObject( methodType );
            object result = methodType.InvokeMember( methodName, BindingFlags.InvokeMethod, null, instance, methodParameters, CultureInfo.InvariantCulture );

            //Libero recursos
            IDisposable dispInstance = instance as IDisposable;
            if ( dispInstance != null )
                dispInstance.Dispose( );
            instance = null;

            return result;
        }

        /// <summary>
        /// </summary>
        /// <param name="method"></param>
        /// <param name="target"></param>
        /// <param name="methodParameters"></param>
        /// <returns></returns>
        public static object InvokeMember( MethodInfo method, object target, params object[] methodParameters )
        {
            return method.Invoke( target, BindingFlags.InvokeMethod, null, methodParameters, CultureInfo.InvariantCulture );
        }
        
        /// <summary>
        /// </summary>
        /// <param name="classInstance"></param>
        /// <param name="methodName"></param>
        /// <returns></returns>
        public static MethodInfo GetMethod( object classInstance, string methodName )
        {
            return classInstance.GetType( ).GetMethod( methodName );
        }
        
        /// <summary>
        /// Crea un nuevo application domain
        /// </summary>
        /// <param name="domainName">Nombre del application domain</param>
        /// <param name="applicationName">Nombre de la aplicacion</param>
        /// <param name="applicationBase">Directorio donde esta contenida la aplicacion</param>
        /// <param name="configurationFile">Path del archivo de configuracion para el nuevo applicaction domain</param>
        /// <returns>El application domain creado</returns>
        public static AppDomain CreateNewDomain( string domainName, string applicationName, string applicationBase, string configurationFile )
        {
            //Configuracion del nuevo application domain
            AppDomainSetup setupInfo = new AppDomainSetup( );
            setupInfo.ApplicationBase = applicationBase;
            setupInfo.ConfigurationFile = configurationFile;
            setupInfo.ApplicationName = applicationName;
            setupInfo.LoaderOptimization = LoaderOptimization.SingleDomain;

            //Creo el nuevo application domain
            AppDomain newDom = AppDomain.CreateDomain( domainName, null, setupInfo );

            //Lo devuelvo
            return newDom;
        }

        /// <summary>
        /// Crea una nueva instancia de un objeto dentro del application domain pasado por parametro
        /// </summary>
        /// <param name="domain">Application Domain donde se creara el objeto</param>
        /// <param name="assemblyName">Nombre del assembly</param>
        /// <param name="objectType">Tipo del objeto a crear</param>
        /// <returns>Una insancia del objeto en el applicacion domain pasado por parametro</returns>
        public static object CreateObjectInAnotherDomain( AppDomain domain, string assemblyName, string objectType )
        {
            return domain.CreateInstanceAndUnwrap( assemblyName, objectType );
        }

        /// <summary>
        /// Devuelve un array con los assemblys que se encuentran en un directorio
        /// </summary>
        /// <param name="assemblysFolder">Directorio donde se deben buscar los assemblys</param>
        /// <param name="attributeType">Tipo de un attributo que deben contener los assemblys que se devuelvan, si este parametro es null no se chequea la presencia de un attributo</param>
        /// <returns>Array con los assemblys que se encuentran el directorio pedido</returns>
        public static Assembly[] GetAssemblys( string assemblysFolder, Type attributeType )
        {
            List<Assembly> result = new List<Assembly>( );

            //Busco todos los assemblys en un directorio
            string[] files = Directory.GetFiles( assemblysFolder, Constants.ServicesFileFilter );
            foreach ( string file in files )
            {
                //Trato de levantar el assembly
                Assembly servAsm = GetAssembly( file, attributeType );

                if ( servAsm != null )
                    result.Add( servAsm );
            }

            return result.ToArray( );
        }

        /// <summary>
        /// Carga y devuelve el assembly indicado en el parametro
        /// </summary>
        /// <param name="assemblyFile">Path completo del assembly a obtener</param>
        /// <param name="attributeType">Tipo de un attributo que deben contener los assemblys que se devuelvan, si este parametro es null no se chequea la presencia de un attributo</param>
        /// <returns></returns>
        public static Assembly GetAssembly( string assemblyFile, Type attributeType )
        {
            Assembly result = null;

            //Trato de levantar el assembly
            Assembly tempAsm = Assembly.LoadFrom( assemblyFile );

            //Si se especifico que el assembly debe tener un atributo custom solo lo agrego a la coleccion a devolver si tiene ese atributo
            if ( attributeType != null )
            {
                object[] attributes = tempAsm.GetCustomAttributes( attributeType, true );
                if ( attributes != null && attributes.GetUpperBound( 0 ) >= 0 )
                    result = tempAsm;
            }
            else
            {
                result = tempAsm;
            }

            return result;
        }

        #endregion
    }
}